package org.windey.client;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

/**
 * 
 * deze klasse wordt gebruikt om de invoer op te slagen naar een lokale XML file
 * of deze file in te lezen
 *
 */
public class WindeyXmlSettings implements WindeySettings
{
	/**
	 * de file
	 */
	private File xmlFile;
	/**
	 * het document
	 */
	private Document dom;
	/**
	 * alle gegevens die we bijhouden
	 */
	private String email;
	private String password;
	private boolean savePassword;
	private String hostname;
	private int port;
	private boolean passwordHasChanged;

	public WindeyXmlSettings(File file)
	{
		this.xmlFile = file;
	}

	/**
	 * om de opgeslagen settings in te laden
	 */
	public void loadSettings() throws SettingsException
	{
		if (xmlFile.exists())
		{
			loadDefaultSettings();
			try
			{
				DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
				DocumentBuilder db = dbf.newDocumentBuilder();
				Document doc = db.parse(xmlFile);
				doc.getDocumentElement().normalize();
				Element acc = (Element)doc.getElementsByTagName("Account").item(0);
				Element server = (Element)doc.getElementsByTagName("Server").item(0);
				
				NodeList l1 = acc.getElementsByTagName("Email");
				Element e1 = (Element) l1.item(0);
				if (e1!=null)
				{
					NodeList n1 = e1.getChildNodes();
					if (n1.getLength() > 0)
					{
						email = ((Node) n1.item(0)).getNodeValue();
					}
				}
				
				NodeList l2 = acc.getElementsByTagName("Paswoord");
				Element e2 = (Element) l2.item(0);
				if (e2!=null)
				{
					NodeList n2 = e2.getChildNodes();
					if (n2.getLength() > 0)
					{
				    	password = ((Node) n2.item(0)).getNodeValue();
				    	savePassword = true;
				    }
					else
					{
						savePassword = false;
					}
				}
				
				NodeList l3 = server.getElementsByTagName("Ip");
				Element e3 = (Element) l3.item(0);
				if (e3!=null)
				{
					NodeList n3 = e3.getChildNodes();
					if (n3.getLength() > 0)
					{
						hostname = ((Node) n3.item(0)).getNodeValue();
					}
				}
			    NodeList l4 = server.getElementsByTagName("Poort");
			    Element e4 = (Element) l4.item(0);
			    if (e4!=null)
			    {
				    NodeList n4 = e4.getChildNodes();
				    if (n4.getLength() > 0)
				    {
				    	port = Integer.parseInt(((Node) n4.item(0)).getNodeValue());
				    }
			    }
			    this.passwordHasChanged = false;
	 		}
			catch (ParserConfigurationException e)
			{
				e.printStackTrace();
				throw new SettingsException();
			}
			catch (SAXException e)
			{
				e.printStackTrace();
				throw new SettingsException();
			}
			catch (IOException e)
			{
				e.printStackTrace();
				throw new SettingsException();
			}
			catch (NullPointerException e)
			{
				e.printStackTrace();
				throw new SettingsException();
			}
		}
		else
		{
			loadDefaultSettings();
		}
	}
	
	/**
	 * als er nog geen file is: default settings inladen
	 */
	public void loadDefaultSettings()
	{
		email = "";
		password = "";
		savePassword = false;
		hostname = "";
		port = 0;
		passwordHasChanged = false;
	}

	/**
	 * om de settings weg te schrijven naar een XML file
	 * worden aantal hulpmethode 1 na 1 gedaan
	 */
	public void saveSettings() throws SettingsException
	{
		try
		{
			createDocument();
			createDOMTree();
			writeToFile();
		}
		catch (ParserConfigurationException e)
		{
			e.printStackTrace();
			throw new SettingsException();
		}
		catch (UnsupportedEncodingException e)
		{
			e.printStackTrace();
			throw new SettingsException();
		}
		catch (NoSuchAlgorithmException e)
		{
			e.printStackTrace();
			throw new SettingsException();
		}
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
			throw new SettingsException();
		}
		catch (TransformerException e)
		{
			e.printStackTrace();
			throw new SettingsException();
		}
	}

	private void createDocument() throws ParserConfigurationException
	{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		dom = db.newDocument();
	}
	
	private void createDOMTree() throws UnsupportedEncodingException, NoSuchAlgorithmException
	{
		Element root = dom.createElement("Settings") ;
		dom.appendChild(root) ;
		Element account = getAccountElement();
		Element server = getServerElement();
		root.appendChild(account);
		root.appendChild(server);
	}
	
	private Element getAccountElement() throws UnsupportedEncodingException, NoSuchAlgorithmException
	{
		Element acc = dom.createElement("Account");
		
		Element email = dom.createElement("Email");
		Element paswoord = dom.createElement("Paswoord");
		
		Text emailtxt = dom.createTextNode(this.email) ;
		email.appendChild(emailtxt) ;
		acc.appendChild(email) ;
		
		if (this.savePassword && this.passwordHasChanged)
		{
			Text paswoordtxt = dom.createTextNode(this.password);
			paswoord.appendChild(paswoordtxt);
			acc.appendChild(paswoord);
		}
		
		return acc ;
	}
	
	private Element getServerElement()
	{
		Element server = dom.createElement("Server");
		Element ip = dom.createElement("Ip") ;
		Text iptxt = dom.createTextNode(this.hostname);
		ip.appendChild(iptxt) ;
		server.appendChild(ip) ;

		Element poort = dom.createElement("Poort") ;
		Text poorttxt = dom.createTextNode(Integer.toString(this.port));
		poort.appendChild(poorttxt);
		server.appendChild(poort);
		
		return server;
	}
	
	/**
	 * wegschrijven naar de file zelf
	 * @throws FileNotFoundException file bestaat niet
	 * @throws TransformerException fout bij omzetten
	 */
	public void writeToFile() throws FileNotFoundException, TransformerException
	{
		Transformer tr;
		try
		{
			tr = TransformerFactory.newInstance().newTransformer();
			tr.setOutputProperty(OutputKeys.INDENT, "yes");
			tr.setOutputProperty(OutputKeys.METHOD,"xml");
			tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
			tr.transform(new DOMSource(dom),new StreamResult(new FileOutputStream(xmlFile)));
		} 
		catch (TransformerConfigurationException e)
		{
			e.printStackTrace();
			throw e;
		} 
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
			throw e;
		} 
		catch (TransformerException e)
		{
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * 
	 * @param text
	 * @return
	 * @throws UnsupportedEncodingException
	 * @throws NoSuchAlgorithmException
	 */
	public static String stringToMD5(String text) throws UnsupportedEncodingException, NoSuchAlgorithmException
	{
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] md5hash = new byte[32];
		md.update(text.getBytes(), 0, text.length());
		md5hash = md.digest();
		return convertToHex(md5hash);
	}
	
	/**
	 * 
	 * @param data
	 * @return
	 */
	private static String convertToHex(byte[] data)
	{
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < data.length; i++) {
			int halfbyte = (data[i] >>> 4) & 0x0F;
			int two_halfs = 0;
			do {
				if ((0 <= halfbyte) && (halfbyte <= 9)) {
					buf.append((char) ('0' + halfbyte));
				}
				else {
					buf.append((char) ('a' + (halfbyte - 10)));
				}
				halfbyte = data[i] & 0x0F;
			}
			while(two_halfs++ < 1);
			}
		return buf.toString();
	}

	/**
	 * @return emailadres
	 */
	public String getEmail()
	{
		return this.email;
	}

	/**
	 * @return de hostname
	 */
	public String getHostname()
	{
		return this.hostname;
	}

	/**
	 * @return het paswoord
	 */
	public String getPassword()
	{
		return this.password;
	}

	/**
	 * @return de poort
	 */
	public int getPort()
	{
		return this.port;
	}

	/**
	 * @return 
	 */
	public boolean getSavePassword()
	{
		return this.savePassword;
	}
	
	/**
	 * @param savePassword
	 */
	public void setSavePassword(boolean savePassword)
	{
		this.savePassword = savePassword;
	}

	/**
	 * @param email  nieuw emailaders
	 */
	public void setEmail(String email)
	{
		this.email = email;
	}

	/**
	 * @param hostname nieuwe hostnaam
	 */
	public void setHostname(String hostname)
	{
		this.hostname = hostname;
	}

	/**
	 * @param password nieuw paswoord
	 */
	public void setPassword(String password)
	{
		try {
			if(!stringToMD5(password).equalsIgnoreCase(this.password))
			{
				this.passwordHasChanged = true;
			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.password = password;
	}

	/**
	 * @param nieuwe poort
	 */
	public void setPort(int port)
	{
		this.port = port;
	}
}
